package seatiger.tomcat.catalina.connector;

import seatiger.tomcat.catalina.Constants;
import seatiger.tomcat.catalina.Globals;
import seatiger.tomcat.catalina.HttpResponse;
import seatiger.tomcat.catalina.connector.http.DefaultHeaders;
import seatiger.tomcat.util.StringManager;

import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基础实现类
 */
@Deprecated
public abstract class HttpResponseBase extends ResponseBase
        implements HttpResponse, HttpServletResponse {

    /**
     * 响应状态码
     */
    protected int status = HttpServletResponse.SC_OK;

    protected HttpResponseFacade facade = new HttpResponseFacade(this);

    protected static StringManager sm =
            StringManager.getManager(Constants.PACKAGE_CONNECTOR);

    /**
     * 报文头的内容
     */
    protected Map<String, List<String>> headers = new ConcurrentHashMap<>();

    protected List<Cookie> cookies = new ArrayList(1);

    protected String message = getStatusMessage(HttpServletResponse.SC_OK);


    @Override
    public void flushBuffer() throws IOException {
        //没有提前刷出，需要先将响应头写出
        if (!isCommitted()){
            sendHeaders();
        }
        super.flushBuffer();
    }

    /**
     * 添加报文头
     * @throws IOException
     */
    @Override
    public void finishResponse() throws IOException {

        if (!isCommitted() &&
                stream == null &&
                writer == null &&
                status >= HttpServletResponse.SC_BAD_REQUEST &&
                contentType == null){
            try {
                setContentType("text/html");
                PrintWriter writer = getWriter();
                writer.println("<html>");
                writer.println("<head>");
                writer.println("<title>Tomcat Error Report</title>");
                writer.println("<br><br>");
                writer.println("<h1>HTTP Status ");
                writer.print(status);
                writer.print(" - ");
                if (message != null) {
                    writer.print(message);
                }else{
                    writer.print(getStatusMessage(status));
                }
                writer.println("</h1>");
                writer.println("</body>");
                writer.println("</html>");
            } catch (IOException e) {
                throw e;
            } catch (Throwable e) {
            }
        }

        sendHeaders();
        super.finishResponse();
    }

    /**
     * 发送响应的报文头
     * @throws IOException
     */
    protected void sendHeaders() throws IOException {
        if (isCommitted()){
            return;
        }
        //将OutputStream 装饰成OutputStreamWriter流
        OutputStreamWriter osr = new OutputStreamWriter(getStream());
        final PrintWriter outputWriter = new PrintWriter(osr);
        //响应行
        outputWriter.write(this.getProtocol());
        outputWriter.print(" ");
        outputWriter.print(status);
        Optional.ofNullable(message)
                        .ifPresent(value -> {
                            outputWriter.print(" ");
                            outputWriter.print(message);
                        });
        outputWriter.print("\r\n");

        //响应头
        if (getContentLength() >= 0){
            printHeader(outputWriter,DefaultHeaders.CONTENT_LENGTH_NAME,getContentLength());
        }
        if(getContentType() != null) {
            printHeader(outputWriter,DefaultHeaders.CONTENT_TYPE_NAME,getContentType());
        }
        headers.keySet().stream().forEach(name ->
                headers.get(name).stream().forEach(value ->
                        printHeader(outputWriter,name,value)));

        //发送cookie
        sendCookie(outputWriter);
        outputWriter.print("\r\n");
        outputWriter.flush();
        committed = true;
    }

    private void sendCookie(PrintWriter outputWriter) {
        HttpServletRequest hreq = (HttpServletRequest)request.getRequest();
        HttpSession session = hreq.getSession(false);
        if (session != null){
            //创建Cookie
            Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,session.getId());
            //这里先统一使用/
            cookie.setPath("/");
            addCookie(cookie);
            cookies.stream().forEach(c -> printHeader(outputWriter,DefaultHeaders.COOKIE_NAME,generateCookieStr(cookie)));
        }
    }

    private String generateCookieStr(Cookie cookie) {
        StringBuilder builder = new StringBuilder();
        builder.append(cookie.getName()).append("=").append(cookie.getValue()).append(";Path=").append(cookie.getPath());
        return builder.toString();
    }

    private void printHeader(PrintWriter outputWriter,String value,Object name){
        outputWriter.print(value);
        outputWriter.print(": ");
        outputWriter.print(name);
        outputWriter.print("\r\n");
    }

    private String getProtocol() {
        return getRequest().getRequest().getProtocol();
    }

    /**
     * 设置报文头
     * @param name
     * @param value
     */
    @Override
    public void setHeader(String name, String value) {
        if (committed){
            return;
        }
        String match = name.toLowerCase();
        switch (match){
            case DefaultHeaders.CONTENT_LENGTH_NAME: {
                int contentLength = -1;
                try {
                    contentLength = Integer.parseInt(value);
                }catch (Exception e){}
                if (contentLength >= 0){
                    setContentLength(contentLength);
                }
                break;
            }
            case DefaultHeaders.CONTENT_TYPE_NAME:{
                setContentType(value);
                break;
            }
            default:{
                List<String> strings = headers.computeIfAbsent(name, k -> new ArrayList<>());
                strings.add(value);
            }
        }
    }

    @Override
    public void sendError(int status) throws IOException {
        sendError(status, getStatusMessage(status));
    }

    @Override
    public void sendError(int status, String message) throws IOException {
        if (isCommitted()) {
            throw new IllegalStateException
                    (sm.getString("httpResponseBase.sendError.ise"));
        }
        this.status = status;
        this.message = message;
        //清除buffer
        resetBuffer();
    }

    @Override
    public void recycle() {
        super.recycle();
        headers.clear();
        cookies.clear();
        status = HttpServletResponse.SC_OK;
    }

    @Override
    public void addCookie(Cookie cookie) {
        if (isCommitted()){
            return;
        }
        synchronized (cookies) {
            cookies.add(cookie);
        }
    }

    @Override
    public ServletResponse getResponse() {
        return facade;
    }

    protected String getStatusMessage(int status) {
        switch (status) {
            case SC_OK:
                return ("OK");
            case SC_ACCEPTED:
                return ("Accepted");
            case SC_BAD_GATEWAY:
                return ("Bad Gateway");
            case SC_BAD_REQUEST:
                return ("Bad Request");
            case SC_CONFLICT:
                return ("Conflict");
            case SC_CONTINUE:
                return ("Continue");
            case SC_CREATED:
                return ("Created");
            case SC_EXPECTATION_FAILED:
                return ("Expectation Failed");
            case SC_FORBIDDEN:
                return ("Forbidden");
            case SC_GATEWAY_TIMEOUT:
                return ("Gateway Timeout");
            case SC_GONE:
                return ("Gone");
            case SC_HTTP_VERSION_NOT_SUPPORTED:
                return ("HTTP Version Not Supported");
            case SC_INTERNAL_SERVER_ERROR:
                return ("Internal Server Error");
            case SC_LENGTH_REQUIRED:
                return ("Length Required");
            case SC_METHOD_NOT_ALLOWED:
                return ("Method Not Allowed");
            case SC_MOVED_PERMANENTLY:
                return ("Moved Permanently");
            case SC_MOVED_TEMPORARILY:
                return ("Moved Temporarily");
            case SC_MULTIPLE_CHOICES:
                return ("Multiple Choices");
            case SC_NO_CONTENT:
                return ("No Content");
            case SC_NON_AUTHORITATIVE_INFORMATION:
                return ("Non-Authoritative Information");
            case SC_NOT_ACCEPTABLE:
                return ("Not Acceptable");
            case SC_NOT_FOUND:
                return ("Not Found");
            case SC_NOT_IMPLEMENTED:
                return ("Not Implemented");
            case SC_NOT_MODIFIED:
                return ("Not Modified");
            case SC_PARTIAL_CONTENT:
                return ("Partial Content");
            case SC_PAYMENT_REQUIRED:
                return ("Payment Required");
            case SC_PRECONDITION_FAILED:
                return ("Precondition Failed");
            case SC_PROXY_AUTHENTICATION_REQUIRED:
                return ("Proxy Authentication Required");
            case SC_REQUEST_ENTITY_TOO_LARGE:
                return ("Request Entity Too Large");
            case SC_REQUEST_TIMEOUT:
                return ("Request Timeout");
            case SC_REQUEST_URI_TOO_LONG:
                return ("Request URI Too Long");
            case SC_REQUESTED_RANGE_NOT_SATISFIABLE:
                return ("Requested Range Not Satisfiable");
            case SC_RESET_CONTENT:
                return ("Reset Content");
            case SC_SEE_OTHER:
                return ("See Other");
            case SC_SERVICE_UNAVAILABLE:
                return ("Service Unavailable");
            case SC_SWITCHING_PROTOCOLS:
                return ("Switching Protocols");
            case SC_UNAUTHORIZED:
                return ("Unauthorized");
            case SC_UNSUPPORTED_MEDIA_TYPE:
                return ("Unsupported Media Type");
            case SC_USE_PROXY:
                return ("Use Proxy");
            case 207:       // WebDAV
                return ("Multi-Status");
            case 422:       // WebDAV
                return ("Unprocessable Entity");
            case 423:       // WebDAV
                return ("Locked");
            case 507:       // WebDAV
                return ("Insufficient Storage");
            default:
                return ("HTTP Response Status " + status);
        }
    }

    @Override
    public int getStatus() {
        return status;
    }

    @Override
    public String getHeader(String name) {
        return Optional.ofNullable(headers.get(name))
                .map(values -> values.get(0))
                .orElse(null);
    }

    /**
     * 重定向
     * @param location
     * @throws IOException
     */
    @Override
    public void sendRedirect(String location) throws IOException {
        if (isCommitted()) {
            throw new IllegalStateException
                    (sm.getString("httpResponseBase.sendRedirect.ise"));
        }
        //清空发送buffer
        resetBuffer();
        try {
            String absolute = toAbsolute(location);
            //设置响应报文头302
            setStatus(SC_MOVED_TEMPORARILY);
            //设置报文头内容
            setHeader("Location", absolute);
        }catch (IllegalArgumentException e){
            setStatus(SC_NOT_FOUND);
        }

    }

    /**
     * 将路径转换成绝对路径
     * @param location
     * @return
     */
    private String toAbsolute(String location) {
        if (location == null) {
            return location;
        }
        URL url;
        try {
            url = new URL(location);
        }catch (MalformedURLException e1){
            HttpServletRequest hreq =
                    (HttpServletRequest) request.getRequest();
            String requrl = HttpUtils.getRequestURL(hreq).toString();
            try {
                url = new URL(new URL(requrl), location);
            } catch (MalformedURLException e2) {
                throw new IllegalArgumentException(location);
            }
        }
        return url.toExternalForm();
    }

    @Override
    public void setStatus(int status) {
        setStatus(status, getStatusMessage(status));
    }

    @Override
    public void setStatus(int status, String message) {
        this.status = status;
        this.message = message;
    }
}
